package pulpfizz.pulp.body;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.color.ColorSpace;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.SinglePixelPackedSampleModel;
import java.awt.image.WritableRaster;
import java.util.Hashtable;

import org.jbox2d.dynamics.Body;

import pulpcore.image.Colors;
import pulpcore.image.CoreGraphics;
import pulpcore.image.CoreImage;
import pulpcore.math.CoreMath;
import pulpcore.sprite.Sprite;

/*
 * TODO: allow for changes in stroke/fill color.
 * Also allow for different colors for sensors.
 * I had this in my local version, but it got borked
 * by an SVN merge, and that part of my file got erased, so...
 */


/**
 * Draws Body objects by creating a Java2D image based on the body definition,
 * and then drawing it using the normal PulpCore sprite container. Created by
 * will Date: Apr 23, 2008 6:22:44 AM
 */
public class Java2DBodySprite extends BodySpriteOld
{
	private CoreImage image;
	private BufferedImage bufferedImage;
	private BodyDrawer bodyDrawer;
	private boolean firstUpdate;
	private float sharpness;

	private boolean clearBeforeDraw = true;
	
	public Java2DBodySprite(Body body, PhysicsLayer physics, float sharpness, Color strokeColor, Color fillColor) {
		this(body, physics, sharpness);
		((SimpleBodyDrawer)(this.bodyDrawer)).setStrokeColor(strokeColor);
		((SimpleBodyDrawer)(this.bodyDrawer)).setFillColor(fillColor);
	}

	public Java2DBodySprite(Body body, PhysicsLayer physics, float sharpness)
	{
		super(body, physics);
		this.sharpness = sharpness;
		this.firstUpdate = true;
		loadImages();
		this.bodyDrawer = new SimpleBodyDrawer();
	}
	
	public Java2DBodySprite(Body body, PhysicsLayer physics, float sharpness, BodyDrawer _bodyDrawer) {
		super(body, physics);
		this.sharpness = sharpness;
		this.firstUpdate = true;
		loadImages();
		this.bodyDrawer = _bodyDrawer;
	}

	public Java2DBodySprite(Body body, PhysicsLayer physics)
	{
		this(body, physics, 1);
	}

	/**
	 * This creates the cached image of the body shapes.
	 * 
	 * @param scaling
	 * @param sharpness
	 */
	public void loadImages()
	{
		int widthInt = width.getAsInt();
		int heightAsInt = height.getAsInt();

		// +4 hack is to allow stroke to fit in image
		// Maybe not best solution, but seems to improve quality for now...
		int imageWidth = (int) (widthInt * sharpness + 4);
		int imageHeight = (int) (heightAsInt * sharpness + 4);

		this.setAnchor(Sprite.CENTER);

		image = new CoreImage(imageWidth, imageHeight, false);
		// Set hotspot to the center point.
		image.setHotspot(imageWidth / 2, imageHeight / 2);
	
		bufferedImage = getBufferedImageView(image);
	}
	
	@Override
	public void setPadding(float padding)
	{
		super.setPadding(padding);
		loadImages();
	}
	
	/**
	 * This does the actual graphics drawing into the BufferedImage using a
	 * Graphics2D context.
	 */
	public void draw(Graphics2D g2)
	{
		float sc = physics.scaling * sharpness;

		// Translate draw coordinates to make 0,0 the center of the image.
		g2.translate(image.getWidth() / 2, image.getHeight() / 2);
		// Translate the draw coordinates to the position offset.
		g2.translate(-offsetX * sharpness, -offsetY * sharpness);
		// Scale draw coordinates from physics world to image coordinates.
		// y axis is inverted, and coordScale is multiplied to scale up the
		// small floating point coordinates to be able
		// to use integer draw methods correctly.
		g2.scale(sc, -sc);

		/*
		 * Delegate the actual drawing to our BodyDrawer.
		 */
		if (body != null && getBodyDrawer() != null)
		{
			getBodyDrawer().drawBody(physics, body, g2);
		}
	}

	public static BufferedImage getBufferedImageView(CoreImage image)
	{
		int w = image.getWidth();
		int h = image.getHeight();

		DirectColorModel colorModel;
		SinglePixelPackedSampleModel sampleModel;

		if (image.isOpaque())
		{
			colorModel = new DirectColorModel(24, 0xff0000, 0x00ff00, 0x0000ff);
			sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
					w, h, new int[] { 0xff0000, 0x00ff00, 0x0000ff });
		} else
		{
			colorModel = new DirectColorModel(ColorSpace
					.getInstance(ColorSpace.CS_sRGB), 32, 0x00ff0000,
					0x0000ff00, 0x000000ff, 0xff000000, true,
					DataBuffer.TYPE_INT);
			sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT,
					w, h, new int[] { 0x00ff0000, 0x0000ff00, 0x000000ff,
							0xff000000 });
		}

		DataBuffer dataBuffer = new DataBufferInt(image.getData(), w * h);
		WritableRaster raster = Raster.createWritableRaster(sampleModel,
			dataBuffer, new Point(0, 0));

		return new BufferedImage(colorModel, raster, true, new Hashtable());
	}

	public boolean needsRedraw()
	{
		if (bodyDrawer != null)
			return bodyDrawer.needsRedraw();
		return false;
	}

	public void update(int elapsedTime)
	{
		super.update(elapsedTime);

		/*
		 * Copied from ImageSprite
		 */
		if (image != null)
		{
			boolean changed = image.update(elapsedTime);
			if (changed)
			{
				setDirty(true);
			}
		}

		/*
		 * Taken from Java2DSprite
		 */
		if (needsRedraw() || firstUpdate)
		{
			Graphics2D g = bufferedImage.createGraphics();
			if (clearBeforeDraw)
			{
				g.setComposite(AlphaComposite.Clear);
				g.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage
						.getHeight());
				g.setComposite(AlphaComposite.SrcOver);
			}
			g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
			g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
				RenderingHints.VALUE_STROKE_PURE);
			g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
				RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
			draw(g);
			g.dispose();
			setDirty(true);
			firstUpdate = false;
		}
	}

	/*
	 * EVERYTHING BELOW WAS YOINKED FROM IMAGESPRITE, BECAUSE WE CAN'T MULTIPLY INHERIT IN JAVA
	 */

	/** direct copy from ImageSprite */
	public void setImage(CoreImage image)
	{
		if (this.image != image)
		{
			this.image = image;
			setDirty(true);
		}
	}

	/** direct copy from ImageSprite */
	protected void drawSprite(CoreGraphics g)
	{
		g.setColor(Colors.GREEN);
		if (image != null)
		{
			g.drawImage(image);
			//            g.drawRect(0,0,this.image.getWidth(),this.image.getHeight());
		} else
		{
			g.drawRect(0, 0, this.width.getAsInt(), this.height.getAsInt());
		}
	}

	/** direct copy from ImageSprite */
	protected int getAnchorX()
	{
		if (image != null && getAnchor() == DEFAULT)
		{
			return CoreMath.toFixed(image.getHotspotX());
		} else
		{
			return super.getAnchorX();
		}
	}

	/** direct copy from ImageSprite */
	protected int getAnchorY()
	{
		if (image != null && getAnchor() == DEFAULT)
		{
			return CoreMath.toFixed(image.getHotspotY());
		} else
		{
			return super.getAnchorY();
		}
	}

	/** direct copy from ImageSprite */
	protected int getNaturalWidth()
	{
		if (image != null)
		{
			return CoreMath.toFixed(image.getWidth());
		} else
		{
			return super.getNaturalWidth();
		}
	}

	/** direct copy from ImageSprite */
	protected int getNaturalHeight()
	{
		if (image != null)
		{
			return CoreMath.toFixed(image.getHeight());
		} else
		{
			return super.getNaturalHeight();
		}
	}

	public BodyDrawer getBodyDrawer()
	{
		return bodyDrawer;
	}

	public void setBodyDrawer(BodyDrawer bodyDrawer)
	{
		this.bodyDrawer = bodyDrawer;
	}
}
